package step

import (
	"math/big"

	"github.com/wanchain/go-mpc/common/hexutil"
	"github.com/wanchain/go-mpc/log"
	"github.com/wanchain/go-mpc/p2p/discover"
	"github.com/wanchain/go-mpc/storeman/osmconf"
	mpcprotocol "github.com/wanchain/go-mpc/storeman/storemanmpc/protocol"
)

// find valid group
type PeerIntersactionStepUni struct {
	BaseStep

	peerRecvMap map[*discover.NodeID]*big.Int
	peerRecv    *big.Int
}

func CreateMpcPeerIntersactionStepUni(totalNum int) *PeerIntersactionStepUni {
	log.SyslogInfo("PeerIntersactionStepUni begin")

	step := &PeerIntersactionStepUni{
		*CreateBaseStep(totalNum, -1),
		make(map[*discover.NodeID]*big.Int, 0),
		nil}

	return step
}

func (step *PeerIntersactionStepUni) InitStep(result mpcprotocol.MpcResultInterface) error {
	log.SyslogDebug("PeerIntersactionStepUni::InitStep begin", "ctx", step.GetMpcContextId())

	step.BaseStep.InitStep(result)
	ret, err := result.GetValue(mpcprotocol.JRJZCollection)

	if err != nil {
		log.SyslogErr("PeerIntersactionStepUni", "InitStep.getValue error", err.Error(), "ctx", step.GetMpcContextId())
		return err
	}
	if len(ret) == 0 {
		log.SyslogErr("PeerIntersactionStepUni", "GetValue len(ret)", len(ret), "ctx", step.GetMpcContextId())
		return err
	}

	ret1, err := result.GetValue(mpcprotocol.BTCMpcPointCollectoin)
	if err != nil {
		log.SyslogErr("PeerIntersactionStepUni", "InitStep.getValue error", err.Error(), "ctx", step.GetMpcContextId())
		return err
	}
	if len(ret) == 0 {
		log.SyslogErr("PeerIntersactionStepUni", "GetValue len(ret)", len(ret), "ctx", step.GetMpcContextId())
		return err
	}

	step.peerRecv, err = osmconf.InterSecByIndexes(&[]big.Int{ret[0], ret1[0]})
	if err != nil {
		log.SyslogErr("PeerIntersactionStepUni", "InterSecByIndexes error", err.Error(), "ctx", step.GetMpcContextId())
		return err
	}
	log.SyslogInfo("PeerIntersactionStepUni::InitStep end", "ctx", step.GetMpcContextId(),
		"peer rcv", hexutil.Encode((*step.peerRecv).Bytes()),
		"JRJZ bigCollection", hexutil.Encode(ret[0].Bytes()),
		"BTCMpcPoint bigCollection", hexutil.Encode(ret1[0].Bytes()))
	result.SetValue(mpcprotocol.BTCRecvCol, []big.Int{*step.peerRecv})
	return nil
}

func (step *PeerIntersactionStepUni) CreateMessage() []mpcprotocol.StepMessage {
	log.SyslogDebug("PeerIntersactionStepUni::CreateMessage begin", "ctx", step.GetMpcContextId())

	message := make([]mpcprotocol.StepMessage, 1)

	message[0].MsgCode = mpcprotocol.MPCMessage
	message[0].PeerID = nil
	message[0].Data = make([]big.Int, 1)
	message[0].Data[0] = *step.peerRecv
	log.SyslogDebug("PeerIntersactionStepUni::CreateMessage", "PeerID", "nil", "peerAlive", hexutil.Encode(message[0].Data[0].Bytes()), "ctx", step.GetMpcContextId())

	log.SyslogDebug("PeerIntersactionStepUni::CreateMessage end", "ctx", step.GetMpcContextId(), "ctx", step.GetMpcContextId())

	return message
}

func (step *PeerIntersactionStepUni) HandleMessage(msg *mpcprotocol.StepMessage) bool {
	log.SyslogInfo("PeerIntersactionStepUni::HandleMessage begin", "ctx", step.GetMpcContextId())

	if len(msg.Data) > 0 {

		log.SyslogInfo("PeerIntersactionStepUni::HandleMessage recv", "data", hexutil.Encode(msg.Data[0].Bytes()), "PeerID", msg.PeerID, "ctx", step.GetMpcContextId())
		log.SyslogInfo("PeerIntersactionStepUni::HandleMessage end", "ctx", step.GetMpcContextId())

		var bigS []big.Int
		bigS = append(bigS, *step.peerRecv)
		bigS = append(bigS, msg.Data[0])
		inter, _ := osmconf.InterSecByIndexes(&bigS)
		_, grpIdStr, _ := step.BaseStep.GetGrpId()
		threshold, _ := osmconf.GetOsmConf().GetThresholdNum(grpIdStr)
		totalNum, _ := osmconf.GetOsmConf().GetTotalNum(grpIdStr)
		hams, _ := osmconf.Hams(inter, totalNum)
		leaderIncluded, _ := osmconf.IsHaming(&msg.Data[0], 0)
		if hams >= threshold && leaderIncluded {
			step.peerRecvMap[msg.PeerID] = &msg.Data[0]
		}
	}
	return true
}

func (step *PeerIntersactionStepUni) FinishStep(result mpcprotocol.MpcResultInterface, mpc mpcprotocol.StoremanManager) error {
	log.SyslogDebug("PeerIntersactionStepUni::FinishStep begin", "ctx", step.GetMpcContextId())

	// compute the inter section and save
	err := step.BaseStep.FinishStep()
	if err != nil {
		return err
	}

	var bigS []big.Int
	for peerId, peerAlice := range step.peerRecvMap {
		log.SyslogDebug("PeerIntersactionStepUni::FinishStep", "peerId", hexutil.Encode(peerId.Bytes()), "collection", hexutil.Encode(peerAlice.Bytes()), "ctx", step.GetMpcContextId())
		bigS = append(bigS, *peerAlice)
	}

	// only intersact with : include leader, include me, >= threshold
	bigInter, err := osmconf.InterSecByIndexes(&bigS)
	if err != nil {
		log.SyslogErr("PeerIntersactionStepUni::FinishStep", "InterSecByIndexes error", err.Error(), "ctx", step.GetMpcContextId())
		return err
	}
	err = result.SetValue(mpcprotocol.MpcPeerRcvIntersaction, []big.Int{*bigInter})
	if err != nil {
		log.SyslogErr("PeerIntersactionStepUni::FinishStep", "MpcPeerRcvIntersaction SetValue error", err.Error(), "ctx", step.GetMpcContextId())
		return err
	}
	log.SyslogInfo("PeerIntersactionStepUni::FinishStep", "intersection", hexutil.Encode(bigInter.Bytes()), "ctx", step.GetMpcContextId())

	return nil
}
